using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows.Forms;
using ALib;
using ALib.Data.CSVOperation;
using ALib.Data.IO;
using ALib.Misc;

namespace TradingPlatform
{
    public abstract class TradingKernel
    {
        private readonly string _tradingClientName;

        private Comm _comm = null;

        public Comm TradingComm
        {
            get {return _comm;}
        }

        public string TradingClientName
        {
            get { return _tradingClientName; }
        }

        protected TickerInfoDb _tickerInfoDb;

        public TickerInfoDb TickerInfoDb
        {
            get { return _tickerInfoDb; }
        }

        protected OrderManagementDb _orderManagementDb;

        public OrderManagementDb OrderManagementDb
        {
            get { return _orderManagementDb; }
        }

        protected SortedList<string, double[]> _lastTicks;

        public SortedList<string, double[]> LastTicks
        {
            get { return _lastTicks; }
        }

        protected SortedList<string, ObjectTuple2<UtcConverter, List<string>>> _tickSeriesList;

        protected SortedList<string, ObjectTuple2<UtcConverter, List<string>>> TickSeriesList
        {
            get { return _tickSeriesList; }
        }

        private readonly bool _startTrading;

        public bool StartTrading
        {
            get { return _startTrading; }
        }

        private readonly string _tradingKernelDescription;

        public string TradingKernelDescription
        {
            get { return _tradingKernelDescription; }
        }

        private readonly List<ObjectTuple3<int, int, string>> _tradingTimingList;

        public List<ObjectTuple3<int, int, string>> TradingTimingList
        {
            get { return _tradingTimingList; }
        }

        protected readonly StrategyClient _strategyClient;

        protected Configuration _tradingKernelConfiguration;
        protected Timer _timer0;

        protected TradingKernel(string tradingClientName)
        {
            _tickerInfoDb = new TickerInfoDb(new FileInfo(@"Configuration\TickerInfo.csv"));
            _orderManagementDb = new OrderManagementDb();
            _lastTicks = new SortedList<string, double[]>();
            _tickSeriesList = new SortedList<string, ObjectTuple2<UtcConverter, List<string>>>();
            _strategyClient = new StrategyClient();
            _tradingKernelConfiguration = new Configuration(@"Configuration\TradingKernel.txt");

            GoodValue.Assert(tradingClientName != string.Empty);
            _tradingClientName = tradingClientName;

            _startTrading = _tradingKernelConfiguration.GetBool(KeyWordsTradingKernel.StartTrading);

            _tradingKernelDescription = GetTradingKernelDescription();
            _tradingTimingList = GetTradingTimingList();

            _comm = new Comm();
        }

        public void StartTradingAllInOne()
        {
            if (_startTrading)
            {
                ConnectStrategy();
                //PreloadData();
                ConnectTradingApi();
                StartTimerBasedTrading();
            }
        }

        #region Trading Gui called funtions.
        public void ConnectStrategy()
        {
            try
            {
                _strategyClient.Connect();
                _strategyClient.Init();
                DisplayStatus(@"Strategy server connected.");
            }
            catch (Exception)
            {
                DisplayStatus("Strategy server connection failure.");
            }
        }

        public void PreloadData()
        {
            SortedList<string, string> strategyTickerNameList = GetStrategyTickerNameList(_tickerInfoDb.TickerNameList);

            if (!_strategyClient.Connected)
            {
                DisplayStatus("Strategy server not connected");
            }
            else
            {
                DisplayStatus("Data preloading...");
                string dataPath = _tradingKernelConfiguration.GetString(KeyWordsTradingKernel.DataPath);
                int preloadDays = _tradingKernelConfiguration.GetInt(KeyWordsTradingKernel.PreloadDays);
                _strategyClient.PreloadData(_tickerInfoDb, strategyTickerNameList, dataPath, preloadDays);
                DisplayStatus("Data preloaded.");
            }
        }

        public abstract void ConnectTradingApi();

        public void LoginApi()
        {
            _comm.Login("CTPClient");
        }

        public void StartTimerBasedTrading()
        {
            try
            {
                int minTimerInterval = _tradingKernelConfiguration.GetInt(KeyWordsTradingKernel.MinTimerInterval);
                int timerInterval0 = _tradingKernelConfiguration.GetInt(KeyWordsTradingKernel.TimerInterval0);
                int timerInterval1 = _tradingKernelConfiguration.GetInt(KeyWordsTradingKernel.TimerInterval1);
                int timerInterval2 = _tradingKernelConfiguration.GetInt(KeyWordsTradingKernel.TimerInterval2);
                int timerInterval3 = _tradingKernelConfiguration.GetInt(KeyWordsTradingKernel.TimerInterval3);

                GoodValue.Assert(minTimerInterval <= timerInterval0);
                GoodValue.Assert(minTimerInterval <= timerInterval1);
                GoodValue.Assert(minTimerInterval <= timerInterval2);
                GoodValue.Assert(minTimerInterval <= timerInterval3);
                GoodValue.Assert(timerInterval1 + timerInterval2 + timerInterval3 + 10 * minTimerInterval <= timerInterval0);

                InitToStart();

                StartTimer0();
            }
            catch
            {
                DisplayStatus("Trading start failure. Could be trading api connection problem.");
            }

        }
        #endregion

        #region Timer based trading
        protected abstract void InitToStart();
        public virtual void UpdateMarketBidAsk(MarketDataBestLevelMessage msg) { }
        public virtual void UpdateMarketTraded(MarketDataTradeMessage msg) { }
        public virtual void UpdateOrderStatus(OrderStatusMessage msg) { }

        private void _timer0_Tick(object sender, EventArgs e)
        {
            Timer0_Process();

            StartTimer1();
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            ((Timer)sender).Stop();

            Timer1_Process();

            StartTimer2();
        }

        void timer2_Tick(object sender, EventArgs e)
        {
            ((Timer)sender).Stop();

            Timer2_Process();

            StartTimer3();
        }

        void timer3_Tick(object sender, EventArgs e)
        {
            ((Timer)sender).Stop();

            Timer3_Process();
        }

        private void StartTimer0()
        {
            _timer0 = new Timer();
            _timer0.Enabled = true;
            _timer0.Interval = _tradingKernelConfiguration.GetInt(KeyWordsTradingKernel.TimerInterval0);
            _timer0.Tick += new EventHandler(_timer0_Tick);
            _timer0.Start();
        }

        private void StartTimer1()
        {
            Timer timer1 = new Timer();
            timer1.Enabled = true;
            timer1.Interval = _tradingKernelConfiguration.GetInt(KeyWordsTradingKernel.TimerInterval1);
            timer1.Tick += new EventHandler(timer1_Tick);
            timer1.Start();
        }

        private void StartTimer2()
        {
            Timer timer2 = new Timer();
            timer2.Enabled = true;
            timer2.Interval = _tradingKernelConfiguration.GetInt(KeyWordsTradingKernel.TimerInterval2);
            timer2.Tick += new EventHandler(timer2_Tick);
            timer2.Start();
        }

        private void StartTimer3()
        {
            Timer timer3 = new Timer();
            timer3.Enabled = true;
            timer3.Interval = _tradingKernelConfiguration.GetInt(KeyWordsTradingKernel.TimerInterval3);
            timer3.Tick += new EventHandler(timer3_Tick);
            timer3.Start();
        }

        protected abstract void Timer0_Process();
        protected abstract void Timer1_Process();
        protected abstract void Timer2_Process();
        protected abstract void Timer3_Process(); 
        #endregion

        #region Display at bottom area.
        public delegate void TradingGuiDisplayEventHandler(string outputString);

        public event TradingGuiDisplayEventHandler OnDisplayStatus;
        public event TradingGuiDisplayEventHandler OnDisplayOmsOrders;
        public event TradingGuiDisplayEventHandler OnDisplayOmsOpenPosition;
        public event TradingGuiDisplayEventHandler OnDisplayErrorMessage;


        public void DisplayStatus(string outputString)
        {
            if (OnDisplayStatus != null)
            {
                OnDisplayStatus(outputString);
            }
        }
        protected void DisplayOmsOrders(string outputString)
        {
            if (OnDisplayOmsOrders != null)
            {
                OnDisplayOmsOrders(outputString);
            }
        }
        protected void DisplayOmsOpenPosition(string outputString)
        {
            if (OnDisplayOmsOpenPosition != null)
            {
                OnDisplayOmsOpenPosition(outputString);
            }
        }
        protected void DisplayErrorMessage(string outputString)
        {
            if (OnDisplayErrorMessage != null)
            {
                OnDisplayErrorMessage(outputString);
            }
        } 
        #endregion

        #region Stratety server, broker, and data related.
        protected abstract void RequestMarketData();

        protected void RequestStrategyServerAndSendOrders()
        {
            SortedList<string, double[]> bars = GetBars();
            GoodValue.Assert(bars.Count > 0);

            DateTime tradingPlatformTime = DateTime.Now;

            SortedList<string, string> strategyTickerNameList = GetStrategyTickerNameList(_tickerInfoDb.TickerNameList);

            SortedList<string, List<string>> strategyStatusAndNewOrders = _strategyClient.Calc(tradingPlatformTime, bars, _orderManagementDb, strategyTickerNameList, _tickerInfoDb);

            foreach (string status in strategyStatusAndNewOrders[KeyWordsStrategy.StrategyStatus])
            {
                DisplayStatus(status);
            }

            if (strategyStatusAndNewOrders.ContainsKey(KeyWordsStrategy.SendOrder))
            {
                foreach (string orderInfo in strategyStatusAndNewOrders[KeyWordsStrategy.SendOrder])
                {
                    DisplayStatus(orderInfo);
                }
            }

            if (strategyStatusAndNewOrders.ContainsKey(KeyWordsStrategy.SendOrder))
            {
                SendOrders(strategyStatusAndNewOrders[KeyWordsStrategy.SendOrder]);
            }
        }

        protected abstract void SendOrders(List<string> orders);

        protected void SaveMarketData()
        {
            string dataPath = _tradingKernelConfiguration.GetString(KeyWordsTradingKernel.DataPath);
            // Save market data.
            foreach (KeyValuePair<string, ObjectTuple2<UtcConverter, List<string>>> tickerName_Series in _tickSeriesList)
            {
                string tickerName = tickerName_Series.Key;
                DateTime exchangeTime = tickerName_Series.Value.Object0.TradingPlatformTimeToExchangeTime(DateTime.Now);
                List<string> series = tickerName_Series.Value.Object1;
                if (series.Count > 0)
                {
                    string date = exchangeTime.ToString(DateTimeFormatType.DateSimple);
                    string time = exchangeTime.ToString(DateTimeFormatType.TimeSimple);
                    FilesIO.ToOneFile(series, new FileInfo(string.Format(@"{0}\{1}\{2}\{3}.csv", dataPath, tickerName, date, time)));
                }
            }
        }
        
        public abstract void Dispose(); 

        protected abstract SortedList<string, string> GetStrategyTickerNameList(List<string> tickerNameList);
        
        private SortedList<string, double[]> GetBars()
        {
            SortedList<string, double[]> result = new SortedList<string, double[]>();

            foreach (string tickerName in _tickerInfoDb.TickerNameList)
            {
                result.Add(tickerName, new double[] { double.NaN, double.NaN, double.NaN, double.NaN, double.NaN });
            }

            foreach (KeyValuePair<string, ObjectTuple2<UtcConverter, List<string>>> tickerName_TickSeries in _tickSeriesList)
            {
                string tickerName = tickerName_TickSeries.Key;

                double[] bar = GetBar(tickerName_TickSeries.Value.Object1);
                result[tickerName] = bar;
            }
            return result;
        }

        private static double[] GetBar(List<string> tickSeries)
        {
            BarFormer barFormer = new BarFormer();

            foreach (string tick in tickSeries)
            {
                double price = double.Parse(tick.Split(new char[] { ',' })[6]);
                if (!double.IsNaN(price) && price > 0)
                {
                    barFormer.UpdateWithTickPrice(price);
                }

                double volume = double.Parse(tick.Split(new char[] { ',' })[7]);
                if (!double.IsNaN(volume) && volume > 0)
                {
                    barFormer.UpdateWithTickVolume(volume);
                }
            }

            double[] result = new double[] { barFormer.Open, barFormer.High, barFormer.Low, barFormer.Close, barFormer.Volume };
            return result;
        }
        #endregion

        private string GetTradingKernelDescription()
        {
            List<string> strategySymbolList = new List<string>();
            foreach (string tickerName in _tickerInfoDb.TickerNameList)
            {
                string symbol = _tickerInfoDb.GetTickerInfoItem(tickerName, TickerInfoItemType.strategySymbol);
                if (!strategySymbolList.Contains(symbol))
                {
                    strategySymbolList.Add(symbol);
                }
            }
            string result = CollectionToString.ListToString(strategySymbolList, "&");

            return result;
        }

        private List<ObjectTuple3<int,int,string>> GetTradingTimingList()
        {
            List<ObjectTuple3<int,int,string>> result = new List<ObjectTuple3<int, int, string>>();
            foreach (string tickerName in _tickerInfoDb.TickerNameList)
            {
                string tradingPlatformTimeZoneIndex = _tickerInfoDb.GetTickerInfoItem(tickerName, TickerInfoItemType.tradingPlatformTimeZoneIndex);
                string exchangeTimeZoneIndex = _tickerInfoDb.GetTickerInfoItem(tickerName, TickerInfoItemType.exchangeTimeZoneIndex);
                string tradingSessionString = _tickerInfoDb.GetTickerInfoItem(tickerName, TickerInfoItemType.tradingHours);

                result.Add(new ObjectTuple3<int, int, string>(int.Parse(tradingPlatformTimeZoneIndex), int.Parse(exchangeTimeZoneIndex), tradingSessionString));
            }

            return result;
        }
    }
}
